package me.zxingcore.qrcode;

import java.util.Map;

import me.zxingcore.BarcodeFormat;
import me.zxingcore.EncodeHintType;
import me.zxingcore.Writer;
import me.zxingcore.WriterException;
import me.zxingcore.common.BitMatrix;
import me.zxingcore.qrcode.decoder.ErrorCorrectionLevel;
import me.zxingcore.qrcode.encoder.ByteMatrix;
import me.zxingcore.qrcode.encoder.Encoder;
import me.zxingcore.qrcode.encoder.QRCode;

public final class QRCodeWriter implements Writer {
    @Override
    public BitMatrix encode(String contents, BarcodeFormat format, int width, int height) throws WriterException {
        return encode(contents, format, width, height, null);
    }

    @Override
    public BitMatrix encode(String contents, BarcodeFormat format, int width, int height,
                            Map<EncodeHintType,?> hints) throws WriterException {

        if (contents.isEmpty()) {
            throw new IllegalArgumentException("Found empty contents");
        }

        if (format != BarcodeFormat.QR_CODE) {
            throw new IllegalArgumentException("Can only encode QR_CODE, but got " + format);
        }

        if (width < 0 || height < 0) {
            throw new IllegalArgumentException("Requested dimensions are too small: " + width + 'x' +
                    height);
        }

        ErrorCorrectionLevel errorCorrectionLevel = null;
        if (hints != null) {
            if (hints.containsKey(EncodeHintType.ERROR_CORRECTION)) {
                errorCorrectionLevel = ErrorCorrectionLevel.valueOf(hints.get(EncodeHintType.ERROR_CORRECTION).toString());
            }
        } else {
            errorCorrectionLevel = ErrorCorrectionLevel.H;
        }

        QRCode code = Encoder.encode(contents, errorCorrectionLevel, hints);
        return renderResult(code, width, height);
    }

    private BitMatrix renderResult(QRCode code, int width, int height) {
        ByteMatrix input = code.getMatrix();
        if (input == null) {
            throw new IllegalStateException();
        }

        // xxx 二维码宽高相等, 即 qrWidth == qrHeight
        int inputWidth = input.getWidth();
        int inputHeight = input.getHeight();

        int qrWidth = inputWidth * (width / inputWidth);
        int qrHeight = inputHeight * (height / inputHeight);

        int outputWidth = Math.max(qrWidth, inputWidth);
        int outputHeight = Math.max(qrHeight, inputHeight);

        int multiple = Math.min(outputWidth / inputWidth, outputHeight / inputHeight);

        BitMatrix output = new BitMatrix(outputWidth, outputHeight);

        for (int inputY = 0, outputY = 0; inputY < inputHeight; inputY++, outputY += multiple) {
            // Write the contents of this row of the barcode
            for (int inputX = 0, outputX = 0; inputX < inputWidth; inputX++, outputX += multiple) {
                if (input.get(inputX, inputY) == 1) {
                    output.setRegion(outputX, outputY, multiple, multiple);
                }
            }
        }

        return output;
    }
}
